import hashlib
import hmac
import json
import requests
import time
import urllib.parse
import sys
import base64

from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin2.api_client.common import (SyncQueryer)
from enum import Enum

class Side(Enum):
  BUY = "buy"
  SELL = "sell"

class InstType(Enum):
  SPOT = "SPOT"
  SWAP = "SWAP"
  FUTURES = "FUTURES"

class TradeMode(Enum):
  CROSS = "cross"
  ISOLATED = "isolated"
  CASH = "cash"

class OrderType(Enum):
  MARKET = "market"
  LIMIT = "limit"
  POST_ONLY = "post_only"
  FOK = "fok"
  IOC = "ioc"
  OPTIMAL_LIMIT_IOC = "optimal_limit_ioc"

class OkexAuth(requests.auth.AuthBase):
  def __init__(self, key):
    self._key = AuthKey.from_file(key)
    self._key.passphrase = self._key.get_value('passphrase')

  def get_sign(self, str_to_be_signed):
    signature = hmac.new(self._key.secret_key_bytes,
                         str_to_be_signed.encode(),
                         digestmod=hashlib.sha256)
    signature = base64.b64encode(signature.digest()).decode()
    return signature

  def __call__(self, req: requests.PreparedRequest):
    assert req.method in ('GET', 'POST', 'DELETE'), req.method
    timestamp = '%.03f' % time.time()
    parsed_result = urllib.parse.urlparse(req.url)
    if req.method == 'GET' and parsed_result.query:
      path = parsed_result.path + '?' + parsed_result.query
    else:
      path = parsed_result.path

    body = req.body.decode() if req.body else ''
    str_to_be_signed = ''.join([timestamp, req.method, path, body])
    headers = {
        'OK-ACCESS-KEY': self._key.access_key,
        'OK-ACCESS-SIGN': self.get_sign(str_to_be_signed),
        'OK-ACCESS-TIMESTAMP': timestamp,
        'OK-ACCESS-PASSPHRASE': self._key.passphrase,
        'Content-Type': 'application/json',
    }
    req.headers.update(headers)
    req.prepare_headers(req.headers)

    # print(req.headers)
    # print(req.body)
    # print(req.url)

    return req


class OkexPrivateClient():
  def __init__(self, key, mea):
    self._key = key
    self._auth = OkexAuth(key)
    self._mea = mea  # be all one
    self._url = "https://www.okx.com"
    self._query = SyncQueryer(api_host=self._url, auth=self._auth)
    if 'Spot' in mea:
      self._inst_type = InstType.SPOT.value
    elif 'Futures' in mea:
      if 'swap' in mea:
        self._inst_type = InstType.SWAP.value
      else:
        self._inst_type = InstType.FUTURES.value
    else:
      assert False, "unsupported mea"

  def get_open_order(self):
    method = "GET"
    path = '/api/v5/trade/orders-pending'
    params = {
        "instType": self._inst_type,
    }
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def get_hist_order(self):
    method = "GET"
    path = '/api/v5/trade/orders-history'
    params = {
        "instType": self._inst_type,
    }
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def get_balance(self, ccy: str = None):
    method = "GET"
    path = '/api/v5/account/balance'
    params = {
        "ccy": ccy,
    }
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def get_position(self, inst_type: str = None):
    method = "GET"
    path = '/api/v5/account/positions'
    params = {
        "instType": inst_type,
    }
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def get_account_config(self):
    method = "GET"
    path = '/api/v5/account/config'
    params = {
    }
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def query_transfer(self):
    method = "GET"
    path = "/api/v5/asset/bills"
    #params = {"type": "20", "before": 1662714252117, "after": 1665306252117}
    params = {"type": "20"}  # type 21:REST_WITHDRAWAL_SUBACCOUNT,  20:REST_DEPOSIT_SUBACCOUNT
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def get_funding_fee(self, start_timestamp: int, end_timestamp: int, limit: int = 200):
    method = "GET"
    path = "/api/v5/account/bills-archive"

    if self._mea == "Futures.Okex.v5":
      inst_type = "FUTURES"
    elif self._mea == "Futures.Okex.v5-swap":
      inst_type = "SWAP"
    else:
      assert False, f"Unexpected mea:{self._mea}!"

    params = {
      "instType": inst_type,
      "type": 8,
      "begin": start_timestamp // 1000000,
      "end": end_timestamp // 1000000,
      "limit": limit
    }
    response = self._query.query(
        method=method, path=path, params=params)
    return response

  def query_deposit_history(self):
    method = "GET"
    path = "/api/v5/asset/deposit-history"
    response = self._query.query(
        method=method, path=path)
    return response

  def query_withdrawal_history(self):
    method = "GET"
    path = "/api/v5/asset/withdrawal-history"
    response = self._query.query(
        method=method, path=path)
    return response

  def submit_order(
    self,
    inst_id: str,
    td_mode: TradeMode,
    side: Side,
    ord_type: OrderType,
    sz: str,
    px: str = None,
    client_ord_id: str = None,
  ):
    assert inst_id, "inst_id must be not None and not empty!"
    assert td_mode, "td_mode must be not None!"
    assert side, "side must be not None!"
    assert ord_type, "ord_type must be not None!"
    assert sz, "sz must be not None and not empty!"

    method = "POST"
    path = "/api/v5/trade/order"
    json = {
      "instId": inst_id,
      "tdMode": td_mode.value,
      "side": side.value,
      "ordType": ord_type.value,
      "sz": sz,
      "px": px,
      "ccy": "USDT",
      "clOrdId": str(int(time.time() * 1000000)),
    }
    if client_ord_id is not None:
      json['clOrdId'] = client_ord_id
    response = self._query.query(method=method, path=path, json=json)
    return response

  def cancel_order(self, inst_id: str, ord_id: str = None, cl_ord_id: str = None):
    assert inst_id, "inst_id must be not None and not empty!"
    assert ord_id or cl_ord_id, "either ord_id or cl_ord_id must be not None!"

    method = "POST"
    path = "/api/v5/trade/cancel-order"
    json = {
      "instId": inst_id,
      "ordId": ord_id,
      "clOrdId": cl_ord_id,
    }
    response = self._query.query(method=method, path=path, json=json)
    return response

  def amend_order(
    self,
    inst_id: str,
    sz: str,
    px: str = None,
    ord_id: str = None,
    client_ord_id: str = None,
  ):
    assert inst_id, "inst_id must be not None and not empty!"
    assert sz, "sz must be not None and not empty!"

    method = "POST"
    path = "/api/v5/trade/amend-order"
    json = {
      "instId": inst_id,
      "newSz": sz,
      "newPx": px,
      "ordId": ord_id,
      "clOrdId": client_ord_id,
      "reqId": str(int(time.time() * 1000000)),
    }
    response = self._query.query(method=method, path=path, json=json)
    print(response.request.url)
    print(response.request.body)
    return response

if __name__ == "__main__":
  key_path = sys.argv[1]
  config = {"key_path": key_path, "mea": "Spot.Okex.v5"}
  
  okex = OkexPrivateClient(key_path, "Spot.Okex.v5")
  #okex = OkexPrivateClient(key_path, "Futures.Okex.v5-swap")
  resp = okex.get_open_order()
  print(json.dumps(resp.json(), indent=2))
  #resp = okex.get_position("SWAP")
  #print(json.dumps(resp.json(), indent=2))
  #resp = okex.submit_order("EOS-USDT", TradeMode.CASH, Side.BUY, OrderType.POST_ONLY, "15", "0.700")
  #print(json.dumps(resp.json(), indent=2))
  #resp = okex.cancel_order("EOS-USDT", "604717211387023360")
  #print(json.dumps(resp.json(), indent=2))
  #resp = okex.amend_order("EOS-USDT", "10", "0.7", "604984130992197638")
  #print(json.dumps(resp.json(), indent=2))
  #resp = okex.get_open_order()
  #print(json.dumps(resp.json(), indent=2))
  # orders = resp['data']
  # for order in orders:
  #   #print(order['ordId'])
  #   resp = okex.cancel_order("XRP-USDT-SWAP", order['ordId'])
  #   resp = resp.json()
  #   print(json.dumps(resp, indent=2))
  #   time.sleep(0.01)
  # for clordid in range(600, 650):
  #    #print(clordid)
  #    resp = okex.submit_order("XRP-USDT-SWAP", TradeMode.CROSS, Side.BUY, OrderType.LIMIT, "1", "0.3", "pl{id}".format(id=clordid))
  #    resp = resp.json()
  #    print(json.dumps(resp, indent=2))
  #    time.sleep(0.01)
